package org.simpleframework.aop;

import lombok.Getter;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.simpleframework.aop.aspect.AspectInfo;
import org.simpleframework.aop.aspect.DefaultAspect;
import org.simpleframework.util.ValidationUtil;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;


/**
 * 使用cglib代理进行切面的织入 此时的切面为一个 list 给一个目标对象的Class进行织入
 *
 * @author: Oliver
 * @time: 2020/12/23/22:34
 */
public class AspectListExecutor implements MethodInterceptor {
    //被织入的对象,目标类
    private Class<?> targetClass;
    @Getter
    private List<AspectInfo> sortedAspectInfoList;


    public AspectListExecutor(Class<?> targetClass, List<AspectInfo> aspectInfoList) {
        this.targetClass = targetClass;
        //此时已经排好顺序
        this.sortedAspectInfoList = sortedAspectInfoList(aspectInfoList);
    }

    private List<AspectInfo> sortedAspectInfoList(List<AspectInfo> aspectInfoList) {
        //comparator接口使用lamda表达式
        Collections.sort(aspectInfoList, (o1, o2) -> o1.getOrder() > o2.getOrder() ? 1 : -1);
        return aspectInfoList;
    }


    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //对要织入切面集合的进行细筛
        accurateMatches(method);

        // 校验是否有切面需要被执行
        if (ValidationUtil.isEmpty(sortedAspectInfoList)) return methodProxy.invokeSuper(o, args);
        //1.按序执行所有before
        beforeAspect(method, args);

        // 2.执行目标类方法 承接返回值
        Object result = null;
        try {
            result = methodProxy.invokeSuper(o, args);
        } catch (Exception e) {
            //3.抛出异常后执行异常处理器
            exceptionAspect(method, args, e);
        }

        // 4.结束前执行 后置处理器
        result = afterAspect(method, args, result);
        return result;
    }

    /**
     * 对sortedAspectInfoList集合进行细筛
     *
     * @param method 被代理的方法
     */
    private void accurateMatches(Method method) {
        if(ValidationUtil.isEmpty(sortedAspectInfoList)){return;}
        Iterator<AspectInfo> it = sortedAspectInfoList.iterator();
        while (it.hasNext()){
            AspectInfo aspectInfo = it.next();
            if(!aspectInfo.getPointcutLocator().accurateMatches(method)){
                it.remove();
            }
        }
    }

    /**
     * 如果被代理方法正常返回，则按照order的顺序降序执行完所有Aspect的afterReturning方法
     *
     * @param method 目标方法
     * @param args   方法参数
     * @return 返回结果
     */
    private Object afterAspect(Method method, Object[] args, Object result) throws Throwable {
        Object res = null;
        for (int i = sortedAspectInfoList.size() - 1; i >= 0; i--) {
            DefaultAspect aspect = sortedAspectInfoList.get(i).getAspect();
            res = aspect.afterReturning(targetClass, method, args, result);
        }
        return res;
    }

    /**
     * 如果被代理方法抛出异常，则按照order的顺序降序执行完所有Aspect的afterThrowing方法
     *
     * @param method 目标方法
     * @param args   方法参数
     */
    private void exceptionAspect(Method method, Object[] args, Exception e) throws Throwable {
        for (int i = sortedAspectInfoList.size() - 1; i >= 0; i--) {
            DefaultAspect aspect = sortedAspectInfoList.get(i).getAspect();
            aspect.afterThrowing(targetClass, method, args, e);
        }
    }

    /**
     * .按照order的顺序升序执行完所有Aspect的before方法
     *
     * @param method
     * @param args
     */
    private void beforeAspect(Method method, Object[] args) throws Throwable {
        for (AspectInfo aspectInfo : sortedAspectInfoList) {
            DefaultAspect aspect = aspectInfo.getAspect();
            aspect.before(targetClass, method, args);
        }
    }
}
